# inverse_solver_window.py (最终版：优化UI布局和启动流程)
import csv
import json
import os
import time
from collections import deque
from typing import Optional
import html
from datetime import datetime
import openpyxl
import numpy as np
import pyqtgraph as pg
import pyqtgraph.exporters
from PyQt6.QtGui import QPainter
from PyQt6.QtCore import pyqtSlot, pyqtSignal, Qt, QDateTime, QTimer
from PyQt6.QtWidgets import (QApplication, QCheckBox, QComboBox, QFileDialog,
                             QGridLayout, QGroupBox, QHBoxLayout, QHeaderView,
                             QLCDNumber, QLabel, QMessageBox, QPushButton,
                             QTableWidget, QTableWidgetItem, QVBoxLayout,
                             QWidget, QTextEdit, QAbstractScrollArea)

from style import DANGER_BUTTON_STYLE, SUCCESS_BUTTON_STYLE, WARNING_BUTTON_STYLE


class InverseSolverWindow(QWidget):
    window_closed = pyqtSignal()
    request_fresh_reading = pyqtSignal()
    start_auto_read_requested = pyqtSignal()
    stop_auto_read_requested = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowFlags(self.windowFlags() | Qt.WindowType.Window)
        self.setWindowTitle("温度反解")
        self.setGeometry(300, 300, 1000, 800)
        self.current_mode = 'single-channel';
        self.is_running = False;
        self.is_connected = False
        self.loaded_polys = {};
        self.loaded_ref_voltages = {};
        self.loaded_equations = {}
        self.plot_counter = 0;
        self.history_length = 10000
        self.time_data = deque(maxlen=self.history_length)
        self.temp_data = [deque(maxlen=self.history_length) for _ in range(12)]
        self.volt_data = [deque(maxlen=self.history_length) for _ in range(12)]
        self.last_raw_voltages = [0.0] * 12
        self.channel_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', '#1a55FF', '#FF1a55']
        self.plot_curves = [];
        self.plot_checkboxes = []
        self.is_waiting_for_ref_reading = False
        self.ref_reading_timer = QTimer(self);
        self.ref_reading_timer.setSingleShot(True);
        self.ref_reading_timer.timeout.connect(self._on_ref_reading_timeout)
        self._init_ui()
        self._update_ui_for_mode()

    def _init_ui(self):
        main_layout = QHBoxLayout(self)
        left_panel = QWidget();
        left_layout = QVBoxLayout(left_panel);
        left_panel.setFixedWidth(400)
        right_panel = QWidget();
        right_layout = QVBoxLayout(right_panel)
        main_layout.addWidget(left_panel);
        main_layout.addWidget(right_panel, 1)

        control_group = QGroupBox("加载与启动");
        control_layout = QGridLayout(control_group)
        self.load_button = QPushButton("加载标定文件");
        self.load_button.clicked.connect(self._on_load_file)
        self.file_label = QLabel("未加载文件");
        self.file_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        self.start_stop_button = QPushButton("开始监控");
        self.start_stop_button.setCheckable(True)
        self.start_stop_button.clicked.connect(self._on_start_stop);
        self.start_stop_button.setEnabled(False)
        # Apply the initial "Start" (green) style
        self.start_stop_button.setStyleSheet(SUCCESS_BUTTON_STYLE)

        self.set_current_ref_button = QPushButton("捕获并修正当前基准电压")
        self.set_current_ref_button.clicked.connect(self._on_set_current_as_reference)
        # Apply the "Warning" (yellow) style
        self.set_current_ref_button.setStyleSheet(WARNING_BUTTON_STYLE)

        control_layout.addWidget(self.load_button, 0, 0, 1, 1);
        control_layout.addWidget(self.file_label, 0, 1, 1, 2)
        control_layout.addWidget(self.start_stop_button, 1, 0, 1, 1);
        control_layout.addWidget(self.set_current_ref_button, 1, 1, 1, 2)
        left_layout.addWidget(control_group)

        # ... The rest of the _init_ui function is unchanged ...
        ref_volt_group = QGroupBox("基准电压 (V_ref)");
        ref_volt_layout = QVBoxLayout(ref_volt_group)
        self.ref_volts_table = QTableWidget();
        self.ref_volts_table.setColumnCount(2)
        self.ref_volts_table.setHorizontalHeaderLabels(["通道", "电压 (V)"]);
        self.ref_volts_table.verticalHeader().setVisible(False)
        self.ref_volts_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch);
        self.ref_volts_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
        self.ref_volts_table.setRowCount(12);
        self.ref_volts_table.setMaximumHeight(160);
        self.ref_volts_table.setSizeAdjustPolicy(QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents)
        for i in range(12):
            ch_item = QTableWidgetItem(f"CH {i + 1}");
            ch_item.setFlags(ch_item.flags() & ~Qt.ItemFlag.ItemIsEditable);
            self.ref_volts_table.setItem(i, 0, ch_item)
        self.ref_volts_table.itemChanged.connect(self._on_ref_volt_changed)
        ref_volt_layout.addWidget(self.ref_volts_table)
        left_layout.addWidget(ref_volt_group)
        data_group = QGroupBox("实时数据");
        data_group_layout = QVBoxLayout(data_group);
        self.single_channel_widget = QWidget();
        single_layout = QGridLayout(self.single_channel_widget);
        single_layout.setContentsMargins(5, 10, 5, 10);
        single_layout.setVerticalSpacing(10);
        self.single_mode_label = QLabel("选择监控通道:");
        self.channel_combo = QComboBox()
        self.channel_combo.currentIndexChanged.connect(self._on_channel_changed)
        self.voltage_label = QLabel("电压 / 基准电压");
        self.voltage_label.setStyleSheet("font-size: 14pt; font-weight: bold; color: #333;");
        self.voltage_label.setAlignment(Qt.AlignmentFlag.AlignCenter);
        self.voltage_label.setMaximumHeight(60)
        self.voltage_display = QLCDNumber();
        self.voltage_display.setDigitCount(7);
        self.voltage_display.setSegmentStyle(QLCDNumber.SegmentStyle.Flat);
        self.voltage_display.setMinimumHeight(80);
        self.voltage_display.setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 5px;")
        self.temp_label = QLabel("反解温度 (°C)");
        self.temp_label.setMaximumHeight(60);
        self.temp_label.setStyleSheet("font-size: 14pt; font-weight: bold; color: #333;");
        self.temp_label.setAlignment(Qt.AlignmentFlag.AlignCenter);
        self.temp_display = QLCDNumber();
        self.temp_display.setDigitCount(7);
        self.temp_display.setSegmentStyle(QLCDNumber.SegmentStyle.Flat);
        self.temp_display.setMinimumHeight(80);
        self.temp_display.setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 5px;")
        single_layout.addWidget(self.single_mode_label, 0, 0);
        single_layout.addWidget(self.channel_combo, 0, 1);
        single_layout.addWidget(self.voltage_label, 1, 0, 1, 2);
        single_layout.addWidget(self.voltage_display, 2, 0, 1, 2);
        single_layout.addWidget(self.temp_label, 3, 0, 1, 2);
        single_layout.addWidget(self.temp_display, 4, 0, 1, 2)
        self.results_table = QTableWidget();
        self.results_table.setColumnCount(3);
        self.results_table.setHorizontalHeaderLabels(["通道", "电压/基准", "温度 (°C)"]);
        self.results_table.verticalHeader().setVisible(False);
        self.results_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch);
        self.results_table.setRowCount(12)
        for i in range(12): self.results_table.setItem(i, 0, QTableWidgetItem(f"CH {i + 1}"))
        data_group_layout.addWidget(self.single_channel_widget);
        data_group_layout.addWidget(self.results_table);
        left_layout.addWidget(data_group, 1)
        equation_group = QGroupBox("拟合曲线方程");
        equation_layout = QVBoxLayout(equation_group);
        self.equation_display = QTextEdit();
        self.equation_display.setReadOnly(True);
        self.equation_display.setPlainText("加载文件后将在此处显示方程。");
        self.equation_display.setStyleSheet("font-family: Courier New; background-color: #fdfdfd;");
        self.equation_display.setMaximumHeight(100);
        equation_layout.addWidget(self.equation_display);
        left_layout.addWidget(equation_group)
        self.plot_controls_group = QGroupBox("绘图显示选择");
        plot_controls_layout = QGridLayout(self.plot_controls_group)
        for i in range(12): checkbox = QCheckBox(f"CH{i + 1}"); checkbox.stateChanged.connect(self._on_plot_checkbox_changed); self.plot_checkboxes.append(
            checkbox); plot_controls_layout.addWidget(checkbox, i // 4, i % 4)
        left_layout.addWidget(self.plot_controls_group)
        time_axis = pg.DateAxisItem(orientation='bottom')
        self.plot_widget = pg.PlotWidget(axisItems={'bottom': time_axis})
        self.plot_widget.plotItem.sigXRangeChanged.connect(self._on_manual_pan_zoom)
        self.plot_widget.setBackground('w');
        self.plot_widget.showGrid(x=True, y=True);
        self.plot_widget.setLabel('left', '温度 (°C)');
        self.plot_widget.setLabel('bottom', '时间');
        self.plot_widget.addLegend()
        for i in range(12): curve = self.plot_widget.plot(pen=self.channel_colors[i], name=f"CH{i + 1}"); curve.setVisible(False); self.plot_curves.append(curve)
        right_layout.addWidget(self.plot_widget, 1)
        plot_actions_group = QGroupBox("绘图控制");
        plot_actions_layout = QHBoxLayout(plot_actions_group);
        plot_actions_layout.addWidget(QLabel("纵轴:"));
        zoom_in_y_btn = QPushButton("缩小(-)");
        zoom_in_y_btn.clicked.connect(lambda: self._zoom_y_axis(-5));
        zoom_out_y_btn = QPushButton("放大(+)");
        zoom_out_y_btn.clicked.connect(lambda: self._zoom_y_axis(5));
        auto_y_btn = QPushButton("自动");
        auto_y_btn.clicked.connect(self._reset_y_auto);
        plot_actions_layout.addWidget(zoom_in_y_btn);
        plot_actions_layout.addWidget(zoom_out_y_btn);
        plot_actions_layout.addWidget(auto_y_btn);
        plot_actions_layout.addSpacing(20);
        plot_actions_layout.addWidget(QLabel("横轴:"));
        self.auto_follow_checkbox = QCheckBox("自动跟随");
        self.auto_follow_checkbox.setChecked(True)
        self.auto_follow_checkbox.stateChanged.connect(self._toggle_auto_follow)
        plot_actions_layout.addWidget(self.auto_follow_checkbox)
        expand_x_btn = QPushButton("范围x2");
        expand_x_btn.clicked.connect(self._expand_x_range)
        plot_actions_layout.addWidget(expand_x_btn);
        plot_actions_layout.addStretch(1);
        clear_plot_btn = QPushButton("清除曲线");
        clear_plot_btn.clicked.connect(self._clear_plot_data);
        save_plot_btn = QPushButton("保存图像与数据");
        save_plot_btn.clicked.connect(self._save_plot_and_data);
        plot_actions_layout.addWidget(clear_plot_btn);
        plot_actions_layout.addWidget(save_plot_btn);
        right_layout.addWidget(plot_actions_group)

    # <--- MODIFIED: Start/Stop logic now triggers auto-read and auto-start ---
    def _on_start_stop(self, checked):
        if checked:
            if not self.loaded_polys: QMessageBox.warning(self, "错误", "无有效的标定数据。"); self.start_stop_button.setChecked(False); return
            if not self.is_connected: QMessageBox.warning(self, "错误", "串口未连接。"); self.start_stop_button.setChecked(False); return
            self.is_running = True;
            self.start_stop_button.setText("停止监控")
            self.start_auto_read_requested.emit()
            self.start_stop_button.setObjectName(DANGER_BUTTON_STYLE)
        else:
            self.is_running = False;
            self.start_stop_button.setText("开始监控")
            self.stop_auto_read_requested.emit()
            self.start_stop_button.setObjectName(SUCCESS_BUTTON_STYLE)

    def _on_start_stop(self, checked):
        if checked:
            # Pre-flight checks
            if not self.loaded_polys:
                QMessageBox.warning(self, "错误", "无有效的标定数据。")
                self.start_stop_button.setChecked(False)
                return
            if not self.is_connected:
                QMessageBox.warning(self, "错误", "串口未连接。")
                self.start_stop_button.setChecked(False)
                return

            # Update state and style
            self.is_running = True
            self.start_stop_button.setText("停止监控")
            self.start_stop_button.setStyleSheet(DANGER_BUTTON_STYLE)  # Set to Red
            self.start_auto_read_requested.emit()
        else:
            # Update state and style
            self.is_running = False
            self.start_stop_button.setText("开始监控")
            self.start_stop_button.setStyleSheet(SUCCESS_BUTTON_STYLE)  # Set back to Green
            self.stop_auto_read_requested.emit()

    @pyqtSlot(bool)
    def on_connection_status_changed(self, is_connected):
        self.is_connected = is_connected
        self._update_button_state()
        if self.is_running and not self.is_connected:
            self.start_stop_button.setChecked(False)  # Stop monitoring if disconnected

        # Attempt to auto-start if conditions are now met
        self._attempt_auto_start()

    def _on_load_file(self):
        # ... (logic is the same, but it calls _attempt_auto_start at the end) ...
        filename, _ = QFileDialog.getOpenFileName(self, "加载标定文件", "", "JSON Files (*.json)")
        if not filename: return
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                data = json.load(f)
            if "fit_results" not in data or "mode" not in data: raise ValueError("文件无效")
            self._reset_state();
            self.current_mode = data["mode"];
            self._update_ui_for_mode()
            ref_volts_data = data.get("reference_voltages", {})
            for ch_str, volt_val in ref_volts_data.items():
                try:
                    ch_index = int(ch_str) - 1
                    if 0 <= ch_index < 12: self.loaded_ref_voltages[ch_index] = float(volt_val)
                except (ValueError, IndexError):
                    pass
            self._populate_ref_volts_table()
            fit_results = data.get("fit_results", {});
            for ch_id_str, fit_data in fit_results.items():
                ch_index = int(ch_id_str) - 1
                self.loaded_polys[ch_index] = np.poly1d(fit_data["fit_params"])
                if "equation" in fit_data: self.loaded_equations[ch_index] = fit_data["equation"]
            self.file_label.setText(os.path.basename(filename));
            self.channel_combo.clear()
            available_channels = sorted(fit_results.keys(), key=int)
            for ch_id in available_channels: self.channel_combo.addItem(f"通道 {ch_id}", userData=int(ch_id) - 1)
            if self.current_mode == '12-channel' or self.current_mode == 'single-channel':
                for i in range(12):
                    ch_id_str = str(i + 1);
                    is_available = ch_id_str in available_channels
                    self.plot_checkboxes[i].setEnabled(is_available)
                    if is_available: self.plot_checkboxes[i].setChecked(True)
            QMessageBox.information(self, "成功", f"'{self.current_mode}' 文件加载成功。")
            self._update_button_state()
            # Attempt to auto-start now that a file is loaded
            self._attempt_auto_start()
        except Exception as e:
            self._reset_state();
            QMessageBox.critical(self, "文件错误", f"无法加载或解析文件:\n{e}")


    def _attempt_auto_start(self):
        """Checks if all conditions for auto-start are met and starts monitoring."""
        if self.is_connected and bool(self.loaded_polys) and not self.is_running:
            # Use a single-shot timer to click the button. This allows the event loop
            # to process the file loading dialog closure before starting monitoring.
            QTimer.singleShot(100, lambda: self.start_stop_button.click())


    def on_voltages_received(self, voltages: list):
        if self.is_waiting_for_ref_reading:
            self.ref_reading_timer.stop();
            self.is_waiting_for_ref_reading = False
            self._apply_new_reference_voltages(voltages)
        if not self.is_running or len(voltages) < 12: return
        self.last_raw_voltages = voltages;
        current_timestamp = time.time();
        self.time_data.append(current_timestamp)
        for i in range(12): self.volt_data[i].append(voltages[i])
        self._recalculate_and_update_displays_and_plots()

    def _on_set_current_as_reference(self):
        if not self.is_running: QMessageBox.warning(self, "提示", "请先开始监控，以捕获当前的电压值。"); return
        reply = QMessageBox.question(self, "确认操作", "程序将立即读取一次硬件电压并设为新基准，是否继续？", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
                                     QMessageBox.StandardButton.No)
        if reply == QMessageBox.StandardButton.Yes:
            self.is_waiting_for_ref_reading = True;
            self.ref_reading_timer.start(3000);
            self.request_fresh_reading.emit()

    def _apply_new_reference_voltages(self, new_ref_voltages: list):
        for i in range(12): self.loaded_ref_voltages[i] = new_ref_voltages[i]
        self._populate_ref_volts_table()
        if self.is_running: self._recalculate_and_update_displays()
        QMessageBox.information(self, "成功", "新的基准电压已根据实时读数设置并更新。")

    def _on_ref_reading_timeout(self):
        if self.is_waiting_for_ref_reading:
            self.is_waiting_for_ref_reading = False;
            QMessageBox.warning(self, "超时", "设置基准电压失败：未能在3秒内收到设备响应。")

    def _recalculate_and_update_displays(self):
        if self.current_mode == 'single-channel':
            ch_index = self.channel_combo.currentData()
            if ch_index is None or ch_index not in self.loaded_polys: return
            voltage = self.last_raw_voltages[ch_index];
            calc_ref_voltage = self.loaded_ref_voltages.get(ch_index)
            if calc_ref_voltage is not None and calc_ref_voltage > 0:
                self.voltage_display.display(f"{(voltage / calc_ref_voltage):.4f}")
            else:
                self.voltage_display.display("Error")
            temp = self._calculate_temperature(voltage, ch_index)
            self.temp_display.display(f"{temp:.2f}" if temp is not None else "Error")
        elif self.current_mode == '12-channel':
            for i in range(12):
                voltage = self.last_raw_voltages[i];
                ref_voltage = self.loaded_ref_voltages.get(i)
                if ref_voltage is not None and ref_voltage > 0:
                    voltage_display_text = f"{(voltage / ref_voltage):.4f}"
                else:
                    voltage_display_text = "N/A"
                self.results_table.setItem(i, 1, QTableWidgetItem(voltage_display_text))
                temp = self._calculate_temperature(voltage, i)
                status_text = f"{temp:.2f}" if temp is not None else ("计算错误" if i in self.loaded_polys else "未标定")
                self.results_table.setItem(i, 2, QTableWidgetItem(status_text))

    def _recalculate_and_update_displays_and_plots(self):
        if self.current_mode == 'single-channel':
            ch_index = self.channel_combo.currentData()
            if ch_index is None or ch_index not in self.loaded_polys: return
            voltage = self.last_raw_voltages[ch_index];
            temp = self._calculate_temperature(voltage, ch_index)
            self.temp_data[ch_index].append(temp if temp is not None else np.nan)
            calc_ref_voltage = self.loaded_ref_voltages.get(ch_index)
            if calc_ref_voltage is not None and calc_ref_voltage > 0:
                self.voltage_display.display(f"{(voltage / calc_ref_voltage):.4f}")
            else:
                self.voltage_display.display("Error")
            self.temp_display.display(f"{temp:.2f}" if temp is not None else "Error")
            self.plot_curves[ch_index].setData(list(self.time_data), list(self.temp_data[ch_index]))
        elif self.current_mode == '12-channel':
            for i in range(12):
                voltage = self.last_raw_voltages[i];
                temp = self._calculate_temperature(voltage, i)
                self.temp_data[i].append(temp if temp is not None else np.nan)
                ref_voltage = self.loaded_ref_voltages.get(i)
                if ref_voltage is not None and ref_voltage > 0:
                    voltage_display_text = f"{(voltage / ref_voltage):.4f}"
                else:
                    voltage_display_text = "N/A"
                self.results_table.setItem(i, 1, QTableWidgetItem(voltage_display_text))
                status_text = f"{temp:.2f}" if temp is not None else ("计算错误" if i in self.loaded_polys else "未标定")
                self.results_table.setItem(i, 2, QTableWidgetItem(status_text))
                if self.plot_checkboxes[i].isChecked(): self.plot_curves[i].setData(list(self.time_data), list(self.temp_data[i]))

    @pyqtSlot(QTableWidgetItem)
    def _on_ref_volt_changed(self, item: QTableWidgetItem):
        if item.column() != 1: return
        row = item.row()
        try:
            new_volt = float(item.text())
            if new_volt > 0:
                self.loaded_ref_voltages[row] = new_volt
                if self.is_running: self._recalculate_and_update_displays()
            else:
                raise ValueError("Voltage must be positive")
        except (ValueError, TypeError):
            old_volt = self.loaded_ref_voltages.get(row)
            self.ref_volts_table.blockSignals(True);
            item.setText(f"{old_volt:.4f}" if old_volt is not None else "");
            self.ref_volts_table.blockSignals(False)

    def _calculate_temperature(self, voltage: float, ch_index: int) -> Optional[float]:
        poly = self.loaded_polys.get(ch_index);
        v_ref = self.loaded_ref_voltages.get(ch_index)
        if poly is None or v_ref is None or v_ref == 0: return None
        y = voltage / v_ref;
        coeffs = poly.coeffs.copy();
        coeffs[-1] -= y
        roots = np.roots(coeffs);
        valid_roots = [np.real(r) for r in roots if np.isreal(r)]
        if len(valid_roots) == 1:
            return valid_roots[0]
        elif len(valid_roots) > 1:
            return min(valid_roots, key=abs)
        else:
            return None

    def _reset_state(self):
        self.file_label.setText("未加载文件");
        self.is_running = False;
        self.start_stop_button.setChecked(False);
        self.start_stop_button.setText("开始监控")
        self.current_mode = None;
        self.loaded_polys.clear();
        self.loaded_ref_voltages.clear();
        self.loaded_equations.clear()
        self.last_raw_voltages = [0.0] * 12
        self._clear_plot_data();
        self._populate_ref_volts_table()
        for curve in self.plot_curves: curve.setVisible(False)
        for checkbox in self.plot_checkboxes: checkbox.setChecked(False); checkbox.setEnabled(False)
        self._update_ui_for_mode();
        self._update_equation_display();
        self._update_button_state()

    def _populate_ref_volts_table(self):
        self.ref_volts_table.blockSignals(True)
        for i in range(12):
            volt = self.loaded_ref_voltages.get(i)
            self.ref_volts_table.setItem(i, 1, QTableWidgetItem(f"{volt:.4f}" if volt is not None else ""))
        self.ref_volts_table.blockSignals(False)

    def _update_equation_display(self):
        if not self.loaded_equations: self.equation_display.setPlainText("加载文件后将在此处显示方程。"); return
        if self.current_mode == 'single-channel':
            ch_index = self.channel_combo.currentData()
            if ch_index is not None:
                eq_str = self.loaded_equations.get(ch_index, "N/A").replace('y = ', '').replace('x', 'T')
                self.equation_display.setHtml(f'<p style="color: {self.channel_colors[ch_index]}; margin: 2px;"><b>CH{ch_index + 1:02d}:</b> y = {html.escape(eq_str)}</p>')
            else:
                self.equation_display.setPlainText("请选择一个通道。")
        else:
            html_parts = []
            for ch_index in sorted(self.loaded_equations.keys()):
                eq_str = self.loaded_equations[ch_index].replace('y = ', '').replace('x', 'T')
                html_parts.append(f'<p style="color: {self.channel_colors[ch_index]}; margin: 2px;"><b>CH{ch_index + 1:02d}:</b> y = {html.escape(eq_str)}</p>')
            self.equation_display.setHtml("".join(html_parts))

    def _on_channel_changed(self):
        self._update_ref_volts_table_visibility(); self._update_equation_display(); self._update_single_channel_plot_visibility()

    def _update_single_channel_plot_visibility(self):
        if self.current_mode != 'single-channel': return
        ch_index = self.channel_combo.currentData()
        if ch_index is not None:
            for i, curve in enumerate(self.plot_curves): curve.setVisible(i == ch_index)

    def _toggle_auto_follow(self, state):
        self.plot_widget.enableAutoRange(axis='x', enable=(state == Qt.CheckState.Checked.value))

    def _expand_x_range(self):
        self.auto_follow_checkbox.setChecked(False)
        x_min, x_max = self.plot_widget.plotItem.viewRange()[0]
        current_width = x_max - x_min
        new_x_max = x_min + (current_width * 2)
        self.plot_widget.setXRange(x_min, new_x_max, padding=0)

    def _on_manual_pan_zoom(self):
        if not self.plot_widget.plotItem.getViewBox().autoRangeEnabled()[0]:
            self.auto_follow_checkbox.blockSignals(True);
            self.auto_follow_checkbox.setChecked(False);
            self.auto_follow_checkbox.blockSignals(False)

    def _reset_y_auto(self):
        self.plot_widget.enableAutoRange(axis='y')

    def _zoom_y_axis(self, amount):
        view_range = self.plot_widget.plotItem.viewRange();
        y_min, y_max = view_range[1]
        new_y_min = y_min - amount;
        new_y_max = y_max + amount
        if new_y_min < new_y_max: self.plot_widget.setYRange(new_y_min, new_y_max, padding=0)

    def _clear_plot_data(self):
        self.plot_counter = 0;
        self.time_data.clear()
        for dq in self.temp_data: dq.clear()
        for dq_v in self.volt_data: dq_v.clear()
        self._refresh_all_curves();

    def _save_plot_and_data(self):
        if not self.time_data:
            QMessageBox.warning(self, "无数据", "图表中没有数据可以保存。")
            return

        current_time_str = QDateTime.currentDateTime().toString("yyyy-MM-dd_hh-mm-ss")
        default_filename = f"temperature_snapshot_{current_time_str}"

        # <--- MODIFIED: Changed file dialog to save as .xlsx ---
        filename, _ = QFileDialog.getSaveFileName(self, "保存图像与数据", default_filename, "Excel Files (*.xlsx)")
        if not filename: return

        # Derive filenames for both PNG and XLSX
        base_filename, _ = os.path.splitext(filename)
        png_filename = base_filename + ".png"
        xlsx_filename = base_filename + ".xlsx"

        # 1. 保存图像 (这部分不变)
        try:
            exporter = pg.exporters.ImageExporter(self.plot_widget.plotItem)
            exporter.export(png_filename)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存图像失败: {e}")
            return

        # <--- MODIFIED: Logic to write data to an Excel file ---
        try:
            # 确定哪些通道的数据是可见的，需要被保存
            visible_channels = []
            if self.current_mode == 'single-channel':
                ch_index = self.channel_combo.currentData()
                if ch_index is not None: visible_channels.append(ch_index)
            else:  # 12-channel
                for i, checkbox in enumerate(self.plot_checkboxes):
                    if checkbox.isChecked(): visible_channels.append(i)

            # 创建一个新的Excel工作簿和工作表
            workbook = openpyxl.Workbook()
            sheet = workbook.active
            sheet.title = "Temperature Data"

            # 写入表头
            sheet.append(['Timestamp', 'Channel_ID', 'Voltage_V', 'Temperature_C'])

            # 写入数据行
            for i in range(len(self.time_data)):
                timestamp = self.time_data[i]
                readable_ts = datetime.fromtimestamp(timestamp).strftime('%m-%d %H:%M:%S')

                for ch_index in visible_channels:
                    volt = self.volt_data[ch_index][i]
                    temp = self.temp_data[ch_index][i]
                    if not np.isnan(temp):
                        # 将时间和通道号作为文本，电压和温度作为数字写入
                        sheet.append([readable_ts, ch_index + 1, volt, temp])

            # 调整列宽以适应内容
            for col in sheet.columns:
                max_length = 0
                column = col[0].column_letter  # 获取列名 (A, B, C...)
                for cell in col:
                    try:
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except:
                        pass
                adjusted_width = (max_length + 2)
                sheet.column_dimensions[column].width = adjusted_width
            # 保存Excel文件
            workbook.save(xlsx_filename)

            QMessageBox.information(self, "成功", f"图像与数据已成功保存:\n{png_filename}\n{xlsx_filename}")

        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存Excel文件失败: {e}")

    def _refresh_all_curves(self):
        for i in range(12):
            if self.plot_checkboxes[i].isChecked() or (self.current_mode == 'single-channel' and self.channel_combo.currentData() == i):
                self.plot_curves[i].setData(list(self.time_data), list(self.temp_data[i]))

    def _update_button_state(self):
        self.start_stop_button.setEnabled(bool(self.loaded_polys) and self.is_connected)

    def _update_ui_for_mode(self):
        is_single = (self.current_mode == 'single-channel');
        self.single_channel_widget.setVisible(is_single);
        self.results_table.setVisible(not is_single);
        self.plot_controls_group.setVisible(not is_single)
        self._update_ref_volts_table_visibility();
        self._update_equation_display()
        if self.current_mode: self._update_single_channel_plot_visibility()

    def _update_ref_volts_table_visibility(self):
        if self.current_mode == 'single-channel':
            current_ch_index = self.channel_combo.currentData()
            if current_ch_index is not None:
                for i in range(12): self.ref_volts_table.setRowHidden(i, i != current_ch_index)
        else:
            for i in range(12): self.ref_volts_table.setRowHidden(i, False)

    def _on_plot_checkbox_changed(self, state):
        for i, checkbox in enumerate(self.plot_checkboxes): self.plot_curves[i].setVisible(checkbox.isChecked())

    def closeEvent(self, event):
        self.hide();
        self.window_closed.emit();
        event.ignore()